package llmcontext

import (
	"context"
	"fmt"

	"github.com/Tencent/WeKnora/internal/logger"
	"github.com/Tencent/WeKnora/internal/models/chat"
	"github.com/Tencent/WeKnora/internal/types/interfaces"
)

// contextManager implements the ContextManager interface
// It handles business logic (compression, token management) and delegates storage to ContextStorage
type contextManager struct {
	storage             ContextStorage                 // Storage backend (Redis, Memory, etc.)
	compressionStrategy interfaces.CompressionStrategy // Compression strategy
	maxTokens           int                            // Maximum tokens allowed in context
}

// NewContextManager creates a new context manager with the specified storage and compression strategy
func NewContextManager(
	storage ContextStorage,
	compressionStrategy interfaces.CompressionStrategy,
	maxTokens int,
) interfaces.ContextManager {
	return &contextManager{
		storage:             storage,
		compressionStrategy: compressionStrategy,
		maxTokens:           maxTokens,
	}
}

// NewContextManagerWithMemory creates a context manager with in-memory storage (for backward compatibility)
func NewContextManagerWithMemory(
	compressionStrategy interfaces.CompressionStrategy,
	maxTokens int,
) interfaces.ContextManager {
	return &contextManager{
		storage:             NewMemoryStorage(),
		compressionStrategy: compressionStrategy,
		maxTokens:           maxTokens,
	}
}

// AddMessage adds a message to the session context
// This method handles the business logic: loading, appending, compression, and saving
func (cm *contextManager) AddMessage(ctx context.Context, sessionID string, message chat.Message) error {
	logger.Infof(ctx, "[ContextManager][Session-%s] Adding message: role=%s, content_length=%d",
		sessionID, message.Role, len(message.Content))

	// Log message content preview
	contentPreview := message.Content
	if len(contentPreview) > 200 {
		contentPreview = contentPreview[:200] + "..."
	}
	logger.Debugf(ctx, "[ContextManager][Session-%s] Message content preview: %s", sessionID, contentPreview)

	// Load existing messages from storage
	messages, err := cm.storage.Load(ctx, sessionID)
	if err != nil {
		logger.Errorf(ctx, "[ContextManager][Session-%s] Failed to load context: %v", sessionID, err)
		return fmt.Errorf("failed to load context: %w", err)
	}

	// Add new message
	beforeCount := len(messages)
	messages = append(messages, message)
	logger.Debugf(ctx, "[ContextManager][Session-%s] Messages count: %d -> %d", sessionID, beforeCount, len(messages))

	// Check if compression is needed
	tokenCount := cm.compressionStrategy.EstimateTokens(messages)
	logger.Debugf(ctx, "[ContextManager][Session-%s] Current token count: %d (max: %d)",
		sessionID, tokenCount, cm.maxTokens)

	if tokenCount > cm.maxTokens {
		logger.Infof(ctx, "[ContextManager][Session-%s] Context exceeds max tokens (%d > %d), applying compression",
			sessionID, tokenCount, cm.maxTokens)
		beforeCompressionCount := len(messages)
		compressed, err := cm.compressionStrategy.Compress(ctx, messages, cm.maxTokens)
		if err != nil {
			logger.Errorf(ctx, "[ContextManager][Session-%s] Failed to compress context: %v", sessionID, err)
			return fmt.Errorf("failed to compress context: %w", err)
		}
		messages = compressed
		afterTokenCount := cm.compressionStrategy.EstimateTokens(messages)
		logger.Infof(ctx, "[ContextManager][Session-%s] Context compressed: %d -> %d messages, %d -> %d tokens",
			sessionID, beforeCompressionCount, len(compressed), tokenCount, afterTokenCount)
	}

	// Save updated messages to storage
	if err := cm.storage.Save(ctx, sessionID, messages); err != nil {
		logger.Errorf(ctx, "[ContextManager][Session-%s] Failed to save context: %v", sessionID, err)
		return fmt.Errorf("failed to save context: %w", err)
	}

	logger.Infof(
		ctx,
		"[ContextManager][Session-%s] Successfully added message (total: %d messages)",
		sessionID,
		len(messages),
	)
	return nil
}

// GetContext retrieves the current context for a session from storage
func (cm *contextManager) GetContext(ctx context.Context, sessionID string) ([]chat.Message, error) {
	logger.Infof(ctx, "[ContextManager][Session-%s] Getting context", sessionID)

	// Load messages from storage
	messages, err := cm.storage.Load(ctx, sessionID)
	if err != nil {
		logger.Errorf(ctx, "[ContextManager][Session-%s] Failed to load context: %v", sessionID, err)
		return nil, fmt.Errorf("failed to load context: %w", err)
	}

	// Calculate token estimate
	tokenCount := cm.compressionStrategy.EstimateTokens(messages)

	logger.Infof(ctx, "[ContextManager][Session-%s] Retrieved %d messages (~%d tokens)",
		sessionID, len(messages), tokenCount)

	// Log message role distribution
	roleCount := make(map[string]int)
	for _, msg := range messages {
		roleCount[msg.Role]++
	}
	logger.Debugf(ctx, "[ContextManager][Session-%s] Message distribution: %v", sessionID, roleCount)

	return messages, nil
}

// ClearContext clears all context for a session from storage
func (cm *contextManager) ClearContext(ctx context.Context, sessionID string) error {
	logger.Infof(ctx, "[ContextManager][Session-%s] Clearing context", sessionID)

	// Delete from storage
	if err := cm.storage.Delete(ctx, sessionID); err != nil {
		logger.Errorf(ctx, "[ContextManager][Session-%s] Failed to clear context: %v", sessionID, err)
		return fmt.Errorf("failed to clear context: %w", err)
	}

	logger.Infof(ctx, "[ContextManager][Session-%s] Context cleared successfully", sessionID)
	return nil
}

// GetContextStats returns statistics about the context
func (cm *contextManager) GetContextStats(ctx context.Context, sessionID string) (*interfaces.ContextStats, error) {
	// Load messages from storage
	messages, err := cm.storage.Load(ctx, sessionID)
	if err != nil {
		logger.Errorf(ctx, "[ContextManager][Session-%s] Failed to load context for stats: %v", sessionID, err)
		return nil, fmt.Errorf("failed to load context: %w", err)
	}

	tokenCount := cm.compressionStrategy.EstimateTokens(messages)

	stats := &interfaces.ContextStats{
		MessageCount:         len(messages),
		TokenCount:           tokenCount,
		IsCompressed:         false, // We'd need to track this explicitly for accurate reporting
		OriginalMessageCount: len(messages),
	}

	logger.Debugf(ctx, "[ContextManager][Session-%s] Context stats: %d messages, ~%d tokens",
		sessionID, stats.MessageCount, stats.TokenCount)

	return stats, nil
}
